/**
 ******************************************************************************
 * @file           : bsp_dht11.c
 * @brief          : DHT11 Driver Implementation
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./Devices/DHT11/bsp_dht11.h"
#include <string.h>

/* Private function prototypes -----------------------------------------------*/
static void dht11_gpio_init(void);
static void dht11_gpio_out(void);
static void dht11_gpio_in(void);
static void dht11_pin_set(uint8_t state);
static uint8_t dht11_pin_read(void);
static void dht11_delay_us(uint32_t us);
static DHT11_Error_e dht11_start(void);
static DHT11_Error_e dht11_read_byte(uint8_t *data);

/* Exported functions --------------------------------------------------------*/

/**
 * @brief       初始化DHT11
 */
DHT11_Error_e dht11_init(void)
{
    /* 初始化GPIO */
    dht11_gpio_init();
    
    log_i("DHT11 initialized");
    return DHT11_OK;
}

/**
 * @brief       读取温湿度数据
 */
DHT11_Error_e dht11_read(DHT11_Data_t *data)
{
    uint8_t buf[5] = {0};
    uint8_t i;
    uint8_t checksum;
    DHT11_Error_e ret;
    
    if (data == NULL)
    {
        return DHT11_ERROR_PARAM;
    }
    
    /* 发送起始信号 */
    ret = dht11_start();
    if (ret != DHT11_OK)
    {
        log_e("Start signal failed");
        return ret;
    }
    
    /* 读取5字节数据 */
    for (i = 0; i < 5; i++)
    {
        ret = dht11_read_byte(&buf[i]);
        if (ret != DHT11_OK)
        {
            log_e("Read byte %d failed", i);
            return ret;
        }
    }
    
    /* 校验和检查 */
    checksum = buf[0] + buf[1] + buf[2] + buf[3];
    if (checksum != buf[4])
    {
        log_e("Checksum error: calc=0x%02X, recv=0x%02X", checksum, buf[4]);
        return DHT11_ERROR_CHECKSUM;
    }
    
    /* 解析数据 */
    data->humidity_int = buf[0];
    data->humidity_dec = buf[1];
    data->temperature_int = buf[2];
    data->temperature_dec = buf[3];
    
    data->humidity = (float)buf[0] + (float)buf[1] / 10.0f;
    data->temperature = (float)buf[2] + (float)buf[3] / 10.0f;
    
    log_d("Temp: %.1f°C, Humi: %.1f%%RH", data->temperature, data->humidity);
    
    return DHT11_OK;
}

/**
 * @brief       单独读取温度
 */
DHT11_Error_e dht11_read_temperature(float *temperature)
{
    DHT11_Data_t data;
    DHT11_Error_e ret;
    
    if (temperature == NULL)
    {
        return DHT11_ERROR_PARAM;
    }
    
    ret = dht11_read(&data);
    if (ret == DHT11_OK)
    {
        *temperature = data.temperature;
    }
    
    return ret;
}

/**
 * @brief       单独读取湿度
 */
DHT11_Error_e dht11_read_humidity(float *humidity)
{
    DHT11_Data_t data;
    DHT11_Error_e ret;
    
    if (humidity == NULL)
    {
        return DHT11_ERROR_PARAM;
    }
    
    ret = dht11_read(&data);
    if (ret == DHT11_OK)
    {
        *humidity = data.humidity;
    }
    
    return ret;
}

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       初始化GPIO
 */
static void dht11_gpio_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    /* 使能GPIO时钟 */
    DHT11_GPIO_CLK_ENABLE();
    
    /* 配置为输出模式 */
    GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
    
    /* 默认拉高 */
    HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET);
}

/**
 * @brief       设置GPIO为输出模式
 */
static void dht11_gpio_out(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
}

/**
 * @brief       设置GPIO为输入模式
 */
static void dht11_gpio_in(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
}

/**
 * @brief       设置引脚电平
 */
static void dht11_pin_set(uint8_t state)
{
    HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, state ? GPIO_PIN_SET : GPIO_PIN_RESET);
}

/**
 * @brief       读取引脚电平
 */
static uint8_t dht11_pin_read(void)
{
    return HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN);
}

/**
 * @brief       微秒延时
 */
static void dht11_delay_us(uint32_t us)
{
    uint32_t ticks = us * (SystemCoreClock / 1000000);
    uint32_t told = SysTick->VAL;
    uint32_t tnow, tcnt = 0;
    
    while (tcnt < ticks)
    {
        tnow = SysTick->VAL;
        if (tnow != told)
        {
            tcnt += (tnow < told) ? (told - tnow) : (SysTick->LOAD - tnow + told);
            told = tnow;
        }
    }
}

/**
 * @brief       发送起始信号
 */
static DHT11_Error_e dht11_start(void)
{
    uint8_t timeout;
    
    /* 输出模式 */
    dht11_gpio_out();
    
    /* 拉低至少18ms */
    dht11_pin_set(0);
    HAL_Delay(20);
    
    /* 拉高20~40us */
    dht11_pin_set(1);
    dht11_delay_us(30);
    
    /* 输入模式 */
    dht11_gpio_in();
    
    /* 等待DHT11拉低（响应信号） */
    timeout = DHT11_TIMEOUT_US;
    while (dht11_pin_read() && timeout--)
    {
        dht11_delay_us(1);
    }
    
    if (timeout == 0)
    {
        return DHT11_ERROR_TIMEOUT;
    }
    
    /* 等待DHT11拉高 */
    timeout = DHT11_TIMEOUT_US;
    while (!dht11_pin_read() && timeout--)
    {
        dht11_delay_us(1);
    }
    
    if (timeout == 0)
    {
        return DHT11_ERROR_TIMEOUT;
    }
    
    /* 等待DHT11拉低（准备发送数据） */
    timeout = DHT11_TIMEOUT_US;
    while (dht11_pin_read() && timeout--)
    {
        dht11_delay_us(1);
    }
    
    if (timeout == 0)
    {
        return DHT11_ERROR_TIMEOUT;
    }
    
    return DHT11_OK;
}

/**
 * @brief       读取一个字节
 */
static DHT11_Error_e dht11_read_byte(uint8_t *data)
{
    uint8_t i;
    uint8_t byte = 0;
    uint8_t timeout;
    
    for (i = 0; i < 8; i++)
    {
        /* 等待低电平结束 */
        timeout = DHT11_TIMEOUT_US;
        while (!dht11_pin_read() && timeout--)
        {
            dht11_delay_us(1);
        }
        
        if (timeout == 0)
        {
            return DHT11_ERROR_TIMEOUT;
        }
        
        /* 延时30us后判断 */
        dht11_delay_us(30);
        
        byte <<= 1;
        if (dht11_pin_read())
        {
            byte |= 0x01;
        }
        
        /* 等待高电平结束 */
        timeout = DHT11_TIMEOUT_US;
        while (dht11_pin_read() && timeout--)
        {
            dht11_delay_us(1);
        }
        
        if (timeout == 0)
        {
            return DHT11_ERROR_TIMEOUT;
        }
    }
    
    *data = byte;
    return DHT11_OK;
}
